package https

import (
	"crypto/md5"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"
)

// 网络请求操作

// 使用GET请求
// 此处仅能使用 `Param` 进行设置请求参数
func (c *CURL) Get() *CURL {
	c.hookBefore() // 请求前操作
	if c.Error != nil {
		return c
	}
	if c.cache_hit {
		c.EndTime = time.Now()
		return c
	}
	// 进行GET请求
	req, err := http.NewRequest("GET", c.getParamUrl("-"), nil)
	if err != nil {
		c.Error = err
		return c
	}
	return c.queryBody(req)
}

// 使用POST请求
// 此处仅能使用 `Param` 进行设置请求参数
func (c *CURL) Post() *CURL {
	c.
		header_default("Content-Type", "application/x-www-form-urlencoded").
		hookBefore()
	if c.Error != nil {
		return c
	}
	if c.cache_hit {
		c.EndTime = time.Now()
		return c
	}
	req, err := http.NewRequest("POST", c.Uri, strings.NewReader(c.getParamUrl("")))
	if err != nil {
		c.Error = err
		return c
	}
	return c.queryBody(req)
}

// 使用POST请求
// 此处使用 `ParamJson` 进行设置请求参数，支持使用 `Param` 设置的参数列表
func (c *CURL) PostJson() *CURL {
	c.hookBefore()
	if c.Error != nil {
		return c
	}
	if c.cache_hit {
		c.EndTime = time.Now()
		return c
	}
	j, _ := json.Marshal(c.ParamJson_quest)
	if len(c.ParamJson_quest) > 0 {
		if v, ok := c.ParamJson_quest["_"]; ok && len(c.ParamJson_quest) == 1 {
			j, _ = json.Marshal(v)
		}
	} else {
		j, _ = json.Marshal(c.Param_quest)
	}
	req, err := http.NewRequest("POST", c.Uri, strings.NewReader(string(j)))
	if err != nil {
		c.Error = err
		return c
	}
	return c.queryBody(req)
}

// 下载文件，将请求的内容保存到本地
//
//	file	保存文件名
func (c *CURL) Download(file string) *CURL {
	if c.Error != nil {
		return c
	}
	// 写入文件
	f, err := os.OpenFile(file, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		c.Error = err
		return c
	}
	defer f.Close()
	f.WriteString(c.Body)
	return c
}

// 下载文件到对外IO中进行重定向
//
//	f	写入对象的IO方法
func (c *CURL) DownloadIO(f io.Writer) *CURL {
	if c.Error != nil {
		return c
	}
	f.Write([]byte(c.Body))
	return c
}

// 获取请求客户端标识
func (c *CURL) getClient() *http.Client {
	c.StartTime = time.Now()
	// 如果传入了Client选项，则直接使用传入的client项
	cl := &http.Client{}
	// 跳过HTTPS证书
	if c.option.https_continue {
		cl.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
	}
	// 设置超时时间
	if c.option.time_out > 0 {
		cl.Timeout = c.option.time_out
	}
	return cl
}

// 获取缓存下标
func (c *CURL) getKey() string {
	return fmt.Sprintf("https_cache_%0x", md5.Sum([]byte(fmt.Sprintf("%T: %v, param: %T: %v, param_json: %T: %v, header: %T: %v",
		c.Uri, c.Uri,
		c.Param_quest, c.Param_quest,
		c.ParamJson_quest, c.ParamJson_quest,
		c.Header_quest, c.Header_quest,
	))))
}

// 获取CURL的GET请求网址
//
//	uri	要拼接的URI前缀，传入-表示使用CURL中的URI网址
func (c *CURL) getParamUrl(uri string) string {
	if c.Error != nil {
		return ""
	}
	dat := url.Values{}
	for i, v := range c.Param_quest {
		dat.Set(i, v)
	}
	if c.Uri == "" {
		c.Error = errors.New("网址不能为空")
		return dat.Encode()
	}
	if uri == "-" {
		uri = c.Uri
	}
	if strings.Contains(uri, "?") {
		uri += "&" + dat.Encode()
	} else {
		uri += "?" + dat.Encode()
	}
	return uri
}

// 从quest结构体中获取到数据并回写到CURL结构体中
//
//	req		请求的待处理操作
func (curl *CURL) queryBody(req *http.Request) *CURL {
	if curl.Error != nil {
		return curl
	}
	defer curl.hookEnd()
	// 设置Header请求头
	for i, v := range curl.Header_quest {
		req.Header.Set(i, v)
	}
	// 执行HTTP请求
	resp, err := curl.getClient().Do(req)
	if err != nil {
		curl.Error = err
		return curl
	}
	defer resp.Body.Close()
	// 获取相应结果
	bodys, err := io.ReadAll(resp.Body)
	if err != nil {
		curl.Error = err
		return curl
	}
	curl.Body = string(bodys)
	curl.HttpCode = resp.StatusCode
	curl.Cookie_quest = resp.Header.Values("set-cookie")
	return curl
}
